Docker 全览
本文由四份 Docker 文档合并整理:工作原理、实战手册、源码 UML 建模、源码仓库关系与阅读指南。
一、架构概览
Docker 采用 客户端-服务端 (Client-Server) 架构:
Docker CLI (docker) ──REST API──▶ Docker Daemon (dockerd) ──▶ Containers
| 组件 | 说明 |
|---|---|
Docker Client (docker) |
CLI 客户端,用户交互入口,通过 REST API 与 daemon 通信 |
Docker Daemon (dockerd) |
后台进程,管理镜像、容器、网络、卷等所有 Docker 对象 |
| Registry | 镜像仓库,负责镜像的存储与分发(默认 Docker Hub) |
| Image | 只读模板,分层存储 |
| Container | 镜像的运行实例,隔离的进程环境 |
| Docker Desktop | 一站化应用,内含 daemon、client、Compose、Kubernetes 等 |
二、核心对象
1. 镜像 (Image)
- 只读模板,包含运行应用所需的一切(代码、运行时、库、环境变量、配置)
- 由
Dockerfile构建,每条指令生成一个镜像层 (layer) - 镜像层是增量 + 缓存的:修改 Dockerfile 重建时,仅变化的层会重新构建
- 基于 UnionFS(联合文件系统)堆叠而成
2. 容器 (Container)
- 镜像的运行实例
-
docker run执行流程:
1. 检查本地是否有镜像 → 无则从 Registry 拉取
2. 创建容器(分配可读写层)
3. 分配网络接口(分配 IP,连接网络)
4. 启动容器,执行指定命令 -
容器本质是宿主机的隔离进程,不是虚拟机
三、底层隔离技术
Docker 利用 Linux 内核特性实现隔离:
| 技术 | 作用 |
|---|---|
| Namespaces | 资源隔离:PID、NET、IPC、MNT、UTS、User 等命名空间,每个容器只看到自己的进程/网络/文件系统 |
| Cgroups (Control Groups) | 资源限制与优先级:限制 CPU、内存、磁盘 I/O 等 |
| UnionFS (如 overlay2) | 分层文件系统:将只读镜像层 + 可写容器层叠加为统一视图 |
四、网络与端口
端口映射与发布
Docker 不会自动为程序分配新端口。端口行为分两层:
| 方式 | 说明 |
|---|---|
-p 8080:80 |
手动映射,将容器 80 端口映射到宿主机 8080 |
-p 192.168.1.1:8080:80 |
绑定指定 IP |
-p 8080:80/udp |
UDP 端口 |
| 不指定 | 端口完全隔离,仅同网络内其他容器可访问,外部无法访问 |
-P / --publish-all |
自动映射,将镜像中 EXPOSE 声明的所有端口随机映射到宿主机高位端口(范围由 /proc/sys/net/ipv4/ip_local_port_range 决定,通常 32768-60999) |
Docker 自动分配的是 IP 地址,不是端口
- 每个容器从桥接网络子网池(默认
172.17.0.0/16)中自动获取一个内部 IP - 自定义网络中,Docker 还内置 DNS 解析,容器名可直接解析为 IP
网络驱动类型
| 驱动 | 说明 |
|---|---|
| bridge | 默认网络驱动,单机容器间通信,容器连接到宿主机虚拟网桥 |
| host | 移除网络隔离,容器直接使用宿主机网络栈 |
| none | 完全隔离容器网络 |
| overlay | 跨多 Docker daemon 的 Swarm Overlay 网络(跨主机通信) |
| ipvlan | 容器共享 MAC,独立 IP |
| macvlan | 容器作为宿主机网络中的独立设备出现,可直接连接物理网络 |
常用操作
docker network ls # 列表
docker network create my-net # 创建 bridge 网络
docker network create --ipv6 --subnet 10.0.0.0/24 my-net # 指定子网
docker network create -d overlay my-overlay # 创建 overlay
docker network rm my-net # 删除
docker network inspect my-net # 详情
docker network connect my-net container # 动态加入
docker network disconnect my-net container # 断开
docker network prune # 清理未使用的
共享网络栈
docker run --network container:redis ... # 与指定容器共享网络命名空间
DNS 配置
docker run --dns 8.8.8.8 ... # 指定 DNS
docker run --dns-search example.com ... # DNS 搜索域
docker run --hostname myhost ... # 主机名
docker run --add-host example.com:1.2.3.4 . # 添加 hosts 记录
自定义子网(daemon.json)
{
"default-address-pools": [
{ "base": "172.17.0.0/16", "size": 24 }
]
}
五、数据存储
Docker 的存储分两层概念:
(1) 容器数据持久化
| 方式 | 管理方式 | 说明 |
|---|---|---|
| Volume | Docker 管理 | 存储在宿主机 /var/lib/docker/volumes/,生产首选 |
| Bind Mount | 宿主机路径 | 直接挂载宿主机任意路径到容器,开发时共享源码 |
| tmpfs Mount | 内存 | 存于内存,容器停止即丢失 |
Volume 命令:
docker volume create myvol # 创建
docker volume ls # 列表
docker volume inspect myvol # 详情
docker volume rm myvol # 删除
docker volume prune # 清理未使用的匿名卷
挂载方式:
# --mount(推荐,更显式)
docker run --mount type=volume,src=myvol,dst=/app ...
docker run --mount type=bind,src=/host/path,dst=/app ...
docker run --mount type=tmpfs,dst=/tmp ...
# -v(简短)
docker run -v myvol:/app ... # volume
docker run -v /host/path:/app ... # bind mount
只读与选项:
docker run -v myvol:/app:ro ... # 只读
docker run --mount src=myvol,dst=/app,readonly ...
docker run -v myvol:/app:volume-nocopy ... # 不复制已有内容
卷子目录挂载:
docker volume create logs
docker run --rm --mount src=logs,dst=/logs alpine mkdir -p /logs/app1 /logs/app2
docker run -d --name app1 \
--mount src=logs,dst=/var/log/app1,volume-subpath=app1 app1:latest
备份与恢复:
# 备份
docker run --rm --volumes-from dbstore -v $(pwd):/backup \
ubuntu tar cvf /backup/backup.tar /dbdata
# 恢复
docker run --rm --volumes-from dbstore2 -v $(pwd):/backup \
ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"
NFS / CIFS 卷:
# NFS
docker volume create --driver local \
--opt type=nfs \
--opt device=:/var/docker-nfs \
--opt o=addr=10.0.0.10 \
nfsvolume
# CIFS
docker volume create --driver local \
--opt type=cifs \
--opt device=//server/backup \
--opt o=addr=server,username=user,password=pass,file_mode=0777,dir_mode=0777 \
cifs-volume
(2) 镜像/容器层存储
- 默认使用
overlay2驱动 - 数据目录:
/var/lib/docker(Linux) - 新版(29.0+)引入 containerd image store,镜像数据存于
/var/lib/containerd
六、容器 vs 虚拟机
虚拟机:每个 VM 有完整 OS + 内核 → 数 GB,启动慢
容器:共享宿主机内核,仅隔离进程+文件 → 数 MB,秒级启动
两者常结合使用:云环境中的 VM 上运行容器运行时,一台 VM 运行多个容器以提高资源利用率。
为什么 Linux 原生就能跑,macOS / Windows 需要 VM?
因为 Linux 的宿主内核本身就提供容器所需的全部机制:命名空间(Namespaces)隔离进程、cgroup 限制资源、overlayfs 做分层文件系统。dockerd 只是直接调用这些内核接口,所以 Linux 上原生运行、不需要任何中间层。
macOS 的内核是 Darwin(XNU),没有这些 Linux 容器原语,所以 Docker 必须先在一个 VM 里装好 Linux 内核,再让 dockerd 跑在 VM 内的 Linux 上。这层 VM 正是 Docker Desktop 存在的意义——也是它比 Linux 原生 Docker 重、关掉应用容器就停的原因。
七、核心工作流
Dockerfile ──build──▶ Image ──push/pull──▶ Registry (Docker Hub)
│
docker run
│
▼
Container (运行实例)
│
docker compose
│
▼
多容器服务编排
八、Dockerfile 核心指令速查
| 指令 | 用途 | 示例 |
|---|---|---|
FROM |
指定基础镜像 | FROM node:20-alpine |
RUN |
构建时执行命令 | RUN apt-get update && apt-get install -y curl |
COPY |
复制文件到镜像 | COPY . /app |
ADD |
复制+自动解压/远程下载 | ADD https://example.com/file.tar.gz /tmp/ |
CMD |
容器默认启动命令 | CMD ["node", "app.js"] |
ENTRYPOINT |
容器入口点(与CMD配合) | ENTRYPOINT ["nginx"] + CMD ["-g", "daemon off;"] |
ENV |
设置环境变量 | ENV NODE_ENV=production |
ARG |
构建参数(仅构建时) | ARG VERSION=1.0 |
WORKDIR |
设置工作目录 | WORKDIR /app |
EXPOSE |
声明端口(文档作用) | EXPOSE 8080 |
VOLUME |
声明挂载点 | VOLUME /data |
USER |
切换用户 | USER node |
LABEL |
添加元数据 | LABEL version="1.0" |
HEALTHCHECK |
健康检查 | HEALTHCHECK CMD curl -f http://localhost/ |
SHELL |
指定 Shell | SHELL ["/bin/bash", "-c"] |
STOPSIGNAL |
停止信号 | STOPSIGNAL SIGTERM |
九、多阶段构建(减小镜像体积)
# syntax=docker/dockerfile:1
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o /server
FROM alpine:3.21
COPY --from=builder /server /server
CMD ["/server"]
复用公共阶段
FROM ubuntu AS base
RUN apt-get update && apt-get install -y shared-tooling
FROM base AS dev
RUN apt-get install -y dev-tooling
FROM base AS prod
COPY --from=build /app /app
十、RUN 挂载增强(BuildKit)
缓存挂载(加速下载)
# Go 模块缓存
RUN --mount=type=cache,target=/root/.cache/go-build go build ...
# apt 缓存
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y gcc
密钥挂载(不写入镜像)
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials \
aws s3 cp s3://... ...
# 构建时传入: docker buildx build --secret id=aws,src=$HOME/.aws/credentials .
SSH 挂载
RUN --mount=type=ssh \
ssh -q -T git@gitlab.com
# 构建时: docker buildx build --ssh default=$SSH_AUTH_SOCK .
网络隔离
RUN --network=none pip install --find-links wheels mypackage # 无网络
RUN --network=host ... # 宿主机网络
十一、容器管理常用命令
生命周期
docker run -d --name myapp -p 8080:80 myimage # 后台运行
docker run -it ubuntu bash # 交互式
docker run --rm alpine echo hello # 运行后自动删除
docker create --name myapp myimage # 创建但不启动
docker stop/start/restart myapp # 停止/启动/重启
docker pause/unpause myapp # 暂停进程
docker rm myapp # 删除容器
docker rm -f myapp # 强制删除
docker exec -it myapp bash # 进入运行中的容器
日志和调试
docker logs -f myapp # 跟踪日志
docker logs --tail 100 myapp # 最后100行
docker inspect myapp # 查看详情
docker stats # 实时资源占用
docker top myapp # 容器内进程
docker cp myapp:/path/file ./local # 拷贝文件
docker diff myapp # 查看文件变更
docker port myapp # 端口映射
镜像管理
docker build -t myapp:v1 . # 构建
docker build --pull --no-cache -t myapp:v1 . # 无缓存构建+拉取最新基础镜像
docker pull/push myimage:tag # 拉取/推送
docker images # 列出镜像
docker rmi myimage:tag # 删除镜像
docker tag src target # 打标签
docker save myimage:tag -o myimage.tar # 导出镜像
docker load -i myimage.tar # 导入镜像
docker image prune # 清理无用镜像
docker system prune -a # 全面清理
十二、资源限制
内存
docker run -m 512M ... # 硬限制 512M
docker run -m 512M --memory-swap -1 ... # 512M 内存,swap 不限
docker run -m 512M ... # 512M 内存 + 512M swap(默认 swap=2x)
docker run -m 512M --memory-swap 1G ... # 512M 内存 + 512M swap
docker run --memory-reservation 200M ...# 软限制
docker run -m 100M --oom-kill-disable . # 禁用 OOM Killer(需配合 -m)
docker run --memory-swappiness=0 ... # 禁用 swap(0-100)
CPU
docker run --cpus=1.5 ... # 最多 1.5 核
docker run --cpu-shares=512 ... # 相对权重(默认 1024)
docker run --cpu-period=50000 --cpu-quota=25000 ... # 50% CPU
docker run --cpuset-cpus="0-2" ... # 绑定 CPU 核心
docker run --cpuset-mems="0-1" ... # 绑定内存节点(NUMA)
IO
docker run --blkio-weight 300 ... # IO 权重 (10-1000)
docker run --blkio-weight-device "/dev/sda:200" ... # 特定设备权重
docker run --device-read-bps /dev/sda:1mb ... # 读速率限制
docker run --device-write-bps /dev/sda:1mb ... # 写速率限制
docker run --device-read-iops /dev/sda:1000 ... # IOPS 读限制
docker run --device-write-iops /dev/sda:1000 ... # IOPS 写限制
十三、Docker Compose
基本结构
services:
web:
build: .
ports:
- "8000:5000"
volumes:
- .:/code
environment:
- REDIS_HOST=redis
depends_on:
- redis
networks:
- frontend
redis:
image: redis:alpine
volumes:
- redis-data:/data
networks:
- frontend
volumes:
redis-data:
networks:
frontend:
常用命令
docker compose up -d # 后台启动
docker compose up --build # 重建并启动
docker compose down # 停止并移除
docker compose down -v # 同时删除卷
docker compose logs -f # 跟踪日志
docker compose logs -f web # 指定服务
docker compose ps # 服务状态
docker compose exec web bash # 进入服务容器
docker compose restart web # 重启单个服务
docker compose pull # 拉取最新镜像
docker compose config # 验证/查看配置
使用外部卷
services:
app:
image: myapp
volumes:
- myvol:/data
volumes:
myvol:
external: true # 引用已存在的卷
十四、Daemon 配置
配置文件位置:
| OS | 路径 |
|---|---|
| Linux | /etc/docker/daemon.json |
| Linux rootless | ~/.config/docker/daemon.json |
| Windows | C:\ProgramData\docker\config\daemon.json |
常用配置:
{
"data-root": "/mnt/docker-data",
"default-address-pools": [
{ "base": "172.17.0.0/16", "size": 24 }
],
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"dns": ["8.8.8.8", "1.1.1.1"],
"registry-mirrors": ["https://mirror.example.com"]
}
手动启动 daemon
dockerd --debug \
--tls=true \
--tlscert=/var/docker/server.pem \
--tlskey=/var/docker/serverkey.pem \
--host tcp://192.168.59.3:2376
十五、权限与安全
# 完全权限(谨慎使用)
docker run --privileged ...
# 精细控制 capabilities
docker run --cap-add=NET_ADMIN ...
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE ...
# 挂载设备
docker run --device=/dev/snd:/dev/snd ...
docker run --device=/dev/sda:/dev/xvdc:r ... # 只读(r)、写(w)、mknod(m)
# 只读根文件系统
docker run --read-only ...
# 添加附加组
docker run --group-add audio --group-add 777 ...
默认 Capabilities(可被 drop)
| Capability | 说明 |
|---|---|
CHOWN |
修改文件 UID/GID |
DAC_OVERRIDE |
绕过文件权限检查 |
FSETID |
修改文件时不清除 SUID/SGID |
KILL |
发信号绕过权限 |
NET_BIND_SERVICE |
绑定 1024 以下端口 |
NET_RAW |
使用 RAW/PACKET 套接字 |
SETGID |
操纵进程 GID |
SETUID |
操纵进程 UID |
SETFCAP |
设置文件 capabilities |
SETPCAP |
修改进程 capabilities |
SYS_CHROOT |
chroot |
常用附加 Capabilities
| Capability | 说明 |
|---|---|
NET_ADMIN |
网络配置操作 |
SYS_ADMIN |
系统管理操作 |
SYS_PTRACE |
ptrace 调试 |
SYS_TIME |
设置系统时钟 |
IPC_LOCK |
锁定内存 |
退出码
| 退出码 | 含义 |
|---|---|
| 0 | 正常退出 |
| 125 | Docker daemon 自身错误 |
| 126 | 容器命令无法执行 |
| 127 | 容器命令不存在 |
| 其他 | 容器内命令的退出码 |
十六、最佳实践
镜像构建
- 使用小型/受信基础镜像:优先 Alpine 或 Docker Official Images
- 多阶段构建:编译与运行分离,减小最终镜像体积
- 合并 RUN 指令 + 清理缓存:
dockerfile RUN apt-get update && apt-get install -y --no-install-recommends \ curl ca-certificates \ && rm -rf /var/lib/apt/lists/* - 使用
.dockerignore:排除 node_modules、.git、build 产物 - 非 root 运行:
dockerfile RUN groupadd -r app && useradd --no-log-init -r -g app app USER app - 固定基础镜像:
FROM alpine:3.21或进一步用 digest pinning - 定期重建:
docker build --pull拉取最新安全补丁 - CI 中构建测试:利用 GitHub Actions 等自动化流程
指令使用
- CMD 用 exec 形式:
CMD ["node", "app.js"] - ENTRYPOINT + CMD:入口脚本 + 默认参数
- WORKDIR 用绝对路径:避免
cd &&模式 - 管道命令加
set -o pipefail:避免管道中错误被忽略 - 参数字母排序:便于维护和 review
- COPY 优于 ADD:ADD 仅用于远程下载/自动解压场景
- ENV 用于运行时变量,ARG 用于构建时:避免环境变量泄露到镜像
容器设计
- 一个容器一个职责:解耦应用,便于横向扩展
- 容器应为临时性:可随时销毁重建
- 健康检查:
HEALTHCHECK确保服务可用性 - 不安装多余软件包:减小体积和攻击面
Dockerfile 示例(综合最佳实践)
# syntax=docker/dockerfile:1
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci --production
COPY . .
FROM node:20-alpine
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
USER app
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/main.js"]
十七、容器环境变量
Docker 自动设置的 Linux 容器环境变量:
| 变量 | 值 |
|---|---|
HOME |
基于 USER 的值 |
HOSTNAME |
容器主机名(可覆盖) |
PATH |
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin |
TERM |
xterm(分配 TTY 时) |
自定义环境变量:
docker run -e "MYVAR=value" ... # 直接设置
docker run -e MYVAR ... # 从宿主机传递
docker run --env-file .env ... # 从文件加载
十八、完整实战:用 Docker 运行 MySQL
用一个完整的 MySQL 示例串联前面的知识点,并与「正常安装的 MySQL」对照,展示 Docker 方式的差异。
启动(与常规安装对比)
常规安装方式:
# Debian/Ubuntu
apt install mysql-server
systemctl start mysql # 作为系统服务启动
mysql -uroot -p # 本机客户端直接连接
Docker 方式:
# 拉取并启动 MySQL 8.0 容器
docker run -d \
--name mysql8 \
-e MYSQL_ROOT_PASSWORD=secret \
-p 3306:3306 \
-v mysql-data:/var/lib/mysql \
mysql:8.0
差异点:
| 对比项 | 常规安装 | Docker |
|---|---|---|
| 启动 | systemctl start mysql,随系统自启 |
docker run 一条命令,进程隔离在容器里 |
| MySQL 监听 | 默认监听宿主机 127.0.0.1:3306 |
默认只在容器内监听,需 -p 3306:3306 才暴露到宿主机 |
| 数据目录 | /var/lib/mysql |
/var/lib/mysql(容器内),配合 volume 持久化 |
| 客户端 | 宿主机自带 mysql 命令 |
宿主机通常没有,需 docker exec 进容器使用 |
使用 MySQL(两种连接方式)
方式一:进入容器执行(推荐,无需额外安装客户端)
docker exec -it mysql8 mysql -uroot -p
# 输入密码后进入 mysql> 提示符,之后的 SQL 操作与常规 MySQL 完全一致
注意:-i 是交互式输入,必须加上;不加 -t 则没有终端提示符。
方式二:宿主机客户端连接(需已映射端口)
mysql -h127.0.0.1 -P3306 -uroot -p
与常规 mysql -uroot -p 的区别:容器版必须显式指定主机和端口(容器不在默认的本地 socket 上)。
创建库与用户
docker exec -it mysql8 mysql -uroot -p
CREATE DATABASE appdb;
CREATE USER 'app'@'%' IDENTIFIED BY 'pass123';
GRANT ALL PRIVILEGES ON appdb.* TO 'app'@'%';
FLUSH PRIVILEGES;
EXIT;
从宿主机/应用连接
# 命令行
mysql -h127.0.0.1 -P3306 -uapp -pappdb
# 应用连接串(JDBC 示例)
jdbc:mysql://127.0.0.1:3306/appdb?user=app&password=pass123
数据持久化与备份
容器删除后数据仍在 mysql-data 卷里,重新 docker run 时挂载同一卷即可恢复。
# 备份(在容器内执行 mysqldump,输出到宿主机)
docker exec mysql8 sh -c 'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" appdb' > backup.sql
停止与删除
docker stop mysql8 # 停止
docker rm mysql8 # 删除容器(命名卷 mysql-data 保留)
docker volume ls # 查看卷,含 mysql-data
docker volume rm mysql-data # 确认不再需要时手动删除数据
十九、镜像仓库(Registry)与私有项目
镜像从哪里来
镜像:docker pull 时从 Registry 拉取,默认是 Docker Hub(docker.io)。国内常配置镜像加速器(registry-mirrors)中转。
Docker 软件本身:Linux 上从官方仓库 https://download.docker.com 安装(配置 apt/yum 源后 apt install docker-ce);macOS 则直接下载 Docker Desktop 安装包。
第一节架构概述中的 Registry 就是镜像来源(默认 Docker Hub),相关配置见「十四、Daemon 配置」的 registry-mirrors。
Registry 里不一定什么都有
Docker Hub 只包含公开的官方镜像与社区上传的镜像,很多私有或企业镜像并不在上面。想要什么镜像:
- 先在本地找:
docker images - 去 Hub 找官方镜像:
docker search mysql - 自己的项目:构建后
push到自己的仓库(见下)
推送自己的项目(完整例子)
# 1. 构建镜像
docker build -t myapp:v1 .
# 2. 打标签(带上仓库地址前缀),再推送
docker tag myapp:v1 <registry>/<namespace>/myapp:v1
docker push <registry>/<namespace>/myapp:v1
# 3. 其他机器拉取
docker pull <registry>/<namespace>/myapp:v1
可选仓库:
| 类型 | 地址示例 | 说明 |
|---|---|---|
| Docker Hub 私有仓库 | docker.io/<username>/myapp |
登录后创建 private repo |
| 自建 registry | 192.168.1.10:5000/myapp |
docker run -d -p 5000:5000 registry,局域网内可用 |
| 云仓库 | 阿里云 ACR / 腾讯云 TCR | 国内访问快,支持私有与免密拉取 |
| GitHub Packages | ghcr.io/<user>/myapp |
与 GitHub 仓库联动,需 token 认证 |
自建 registry 默认走 HTTP,若客户端需 HTTPS 可加 --insecure-registry 或配置 TLS。
二十、容器文件系统与数据存放位置
容器不是一台完整主机
容器不能完全当作一台「完整主机」来使用:
- 共享宿主内核:只有进程、网络、文件系统、权限是隔离的,没有自己的内核;
systemctl、cron、SSH 这类系统服务默认不可用。 - 一个容器 = 一个进程/一个职责:容器是临时的、可随时销毁重建的(见「十六、最佳实践」)。
- 调试不进容器装工具:用
docker exec -it进入,日志用docker logs查看,不在容器内装 SSH / 编辑器。
容器内目录结构
- 遵循标准 Linux FHS,与普通发行版一致:
/etc、/usr、/bin、/var、/lib等。 - 底层是分层存储(overlayfs):基础镜像层只读,容器顶部一层可写。写在容器可写层(非卷内)的数据,容器删除即丢失。
- 约定俗成的布局:应用代码 →
WORKDIR /app;数据目录 → 挂 volume(如 MySQL 用/var/lib/mysql)。
Volume 数据实际落在宿主机哪里
挂 volume 本质就是把数据放在宿主机磁盘上,但分两种形态:
| 类型 | 宿主机位置 | 说明 |
|---|---|---|
| Volume(推荐) | /var/lib/docker/volumes/<卷名>/_data |
Docker 托管,藏在 Docker 数据目录里;docker rm 删容器后卷仍保留,需 docker volume rm 才删数据;用 docker volume inspect 查真实位置。 |
| Bind Mount | 你指定的宿主路径 | 如 -v /home/brian/mysql:/var/lib/mysql,宿主机可直接看到/编辑;常用于开发时共享源码。 |
容器路径 vs 宿主路径(以 MySQL 为例)
docker run -d \
--name mysql8 \
-e MYSQL_ROOT_PASSWORD=secret \
-p 3306:3306 \
-v mysql-data:/var/lib/mysql \
mysql:8.0
- 冒号后面
/var/lib/mysql是容器内路径:MySQL 官方镜像固定把数据写在这里,容器进程往这里写。 - 冒号前面
mysql-data是宿主机那边(卷名)。 - 实际落盘:宿主机
/var/lib/docker/volumes/mysql-data/_data。容器删除后数据仍在卷里,重新docker run挂同一卷即可恢复。
不挂 Volume 时数据何时丢
- 停止(
docker stop)不丢:容器只是暂停运行,可写层仍保留,docker start后再起数据还在。 - 删除(
docker rm)才丢:可写层随之销毁,未挂卷的数据就没了;用--rm运行则容器退出时自动删除。 - 卷里的数据不受影响:
docker rm后 volume 数据仍在。 - 生产升级镜像往往要重建容器(rm + run),这正是要挂 volume 的原因——容器可随时删,数据不能丢。
二十一、容器进程 vs 直接进程
容器本质是宿主机的隔离进程,所以 ps/top/htop 都能看到并操作容器内进程。但从进程管理工具角度看,两者有明显差异:
| 管理工具 | 直接启动的进程 | Docker 容器进程 |
|---|---|---|
ps / top / htop |
正常可见 | 可见(宿主 PID 视角) |
systemctl |
可管理(配 systemd unit) | 不可见,挂在 dockerd → containerd → containerd-shim 进程树下,只能用 docker 命令管理 |
kill |
按宿主 PID 发送信号 | 对普通进程有效;容器内 PID 1 可能忽略 SIGINT/SIGTERM 等默认信号,建议用 docker stop |
- PID 命名空间:宿主机
ps看到的是宿主 PID;容器内看到的 PID 是 namespace 内视角,两者不一定相同(容器内 PID 1 = 主进程,宿主机上对应别的 PID) - 日常查看(ps/top/htop)两者无差别;启停管理用
docker compose up/down,不是 systemd
二十二、源码架构 UML 建模
基于 moby/moby、docker/cli、containerd/containerd、opencontainers/runc、moby/buildkit 五个仓库源码梳理。
UML 标准依据:UML 2.5.1 (formal-17-12-05)。
一、组件图(Component Diagram)—— Docker 系统架构
二、部署图(Deployment Diagram)—— 物理部署拓扑
三、类图(Class Diagram)—— 核心域模型
3.1 dockerd 核心类图
3.2 graphdriver / overlay2 存储驱动类图
3.3 containerd 核心接口类图
3.4 BuildKit solver 核心类图
四、序列图(Sequence Diagram)—— 关键交互
4.1 docker run 完整调用链
4.2 docker build 调用链(BuildKit 路径)
4.3 容器退出与重启流程
五、状态机图(State Machine Diagram)—— 容器状态转移
六、用例图(Use Case Diagram)—— Docker 系统功能
七、包图(Package Diagram)—— 代码模块依赖关系
7.1 moby 核心包依赖
7.2 五仓库整体依赖关系
7.3 BuildKit 内部包依赖
八、通信图(Communication Diagram)—— 组件协作关系
8.1 镜像构建协作
8.2 容器运行协作
九、关键设计模式与架构决策总结
| 层级 | 仓库 | 核心抽象 | 设计模式 |
|---|---|---|---|
| CLI | docker/cli | Cobra Command + DockerCli | Command 模式、Facade |
| API Gateway | moby/moby | REST API Router | Front Controller、Adapter |
| Engine | moby/moby | Daemon + ImageService interface | Strategy(graphdriver vs containerd snapshotter) |
| Runtime | containerd/containerd | Plugin-based gRPC services | Plugin 架构、桥接模式 |
| Runtime | opencontainers/runc | libcontainer + State FSM | 状态模式、Builder 模式(Spec -> Config) |
| Build | moby/buildkit | Vertex -> Edge -> Op -> Solver | DAG 求解器、缓存模式、Pipeline |
| Storage | moby/moby | graphdriver.ProtoDriver | 策略模式(overlay2/aufs/btrfs/devicemapper) |
| Storage | containerd/containerd | snapshots.Snapshotter + content.Store | 内容寻址存储 (CAS) |
| Network | moby/moby | libnetwork.Controller | Driver 模式(bridge/host/overlay/macvlan) |
十、对照 UML 规范关键点说明
| UML 元素 | 本图中的使用 | 规范依据 (UML 2.5.1) |
|---|---|---|
| 组件图 | 各仓库独立组件(runc, containerd, dockerd, buildkit, cli) | §11 Structured Classifiers |
| 部署图 | 物理节点与软件部署映射(device/executable/artifact) | §19 Deployments |
类图 <<interface>> + 实现 |
ImageService, Driver, Vertex, Worker 等 | §9 Classification |
组合 *-- |
Container *-- State,State 随 Container 创建/销毁 | §9.5.3 composite aggregation |
聚合 o-- |
Solver 1..* Vertex, Solver 持有 Vertex 但 Vertex 可独立 | §9.5.3 shared aggregation |
| 序列图 alt/opt/loop | docker run 的 alt(本地无镜像)、opt(AutoRemove) | §17 Interactions CombinedFragment |
| 状态机转移+守卫 | [restartPolicy matches] [wait duration elapsed] |
§14 StateMachines |
| 用例图 includes/extends | compose 依赖 create/start/network/volume | §18 UseCases |
| 包图依赖(无环) | 所有依赖均自上向下,无循环依赖 | §12 Packages |
二十三、源码仓库关系与阅读指南
本文档基于官方源码仓库,梳理 Docker 各组件之间的调用关系及推荐阅读顺序。
1. 仓库清单
| 仓库 | 本地路径 | 职责 |
|---|---|---|
| moby/moby | repos/moby |
Docker 引擎核心(daemon + API) |
| docker/cli | repos/cli |
docker 命令行客户端 |
| opencontainers/runc | repos/runc |
OCI 标准容器运行时 |
| moby/buildkit | repos/buildkit |
新一代镜像构建引擎 |
| containerd/containerd | repos/containerd |
容器运行时管理器 |
2. 调用关系图
用户
│
▼
┌──────────────┐ Unix Socket / TCP ┌──────────────────┐
│ docker CLI │ ────────────────────────▶ │ dockerd (moby) │
│ (repos/cli) │ REST API │ (repos/moby) │
└──────────────┘ └───────┬──────────┘
│
┌────────────────────────┼────────────────────────┐
│ grpc │ grpc │ gRPC
▼ ▼ ▼
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ containerd │ │ BuildKit │ │ Registry │
│ (containerd) │ │ (repos/buildkit)│ │ (外部) │
└──────┬───────┘ └──────────────────┘ └──────────────┘
│ fork/exec + shim
▼
┌──────────────┐
│ runc │
│ (repos/runc) │
└──────┬───────┘
│ Linux namespaces / cgroups
▼
┌──────────────┐
│ Container │
│ Process │
└──────────────┘
关键调用链:
docker run nginx
→ docker CLI 发送 POST /containers/create 到 dockerd
→ dockerd 调用 containerd gRPC API
→ containerd 创建 shim 进程
→ shim 调起 runc create + start
→ runc 设置 namespace / cgroup,exec 用户进程
3. 仓库关系详解
3.1 dockerd (moby) → containerd
- dockerd 把容器生命周期管理(create/start/stop/kill)委托给 containerd
- 通过 containerd 的 gRPC TaskService 接口调用
- 关键代码:
moby/daemon/containerd/— containerd 集成层 - dockerd 自身管理:镜像元数据、网络、卷、Swarm 编排、REST API
3.2 containerd → runc
- containerd 不直接启停容器进程,而是通过 OCI runtime(默认 runc)
- containerd fork 出 shim 进程,shim 再调 runc
- shim 作用:让容器进程与 containerd 解耦,containerd 重启不影响运行中的容器
- 关键代码:
containerd/cmd/containerd-shim-runc-v2/
3.3 dockerd (moby) → BuildKit
- 从 Docker 23+ 起,
docker build默认使用 BuildKit - dockerd 通过 gRPC 连接 buildkitd 执行镜像构建
- 关键代码:
moby/daemon/builder/+buildkit/frontend/dockerfile/
3.4 docker CLI → dockerd
- CLI 完全不直接操作容器,所有逻辑通过调用 dockerd REST API
- 关键代码:
cli/cli/command/container/run.go→moby/client/
4. 推荐阅读顺序(由浅入深)
第一阶段:看懂 runc — 理解"容器到底是什么"
目标:理解 Linux 容器底层原理(namespace / cgroup / rootfs)
| 顺序 | 文件 | 内容 |
|---|---|---|
| 1 | runc/main.go |
CLI 入口,urfave/cli 框架 |
| 2 | runc/run.go |
runc run 命令,创建+启动容器 |
| 3 | runc/create.go |
runc create,仅创建不运行 |
| 4 | runc/start.go |
runc start,启动已创建的容器 |
| 5 | runc/libcontainer/ |
核心:Linux namespace 设置、cgroup 管理 |
| 6 | runc/libcontainer/process_linux.go |
容器进程启动逻辑 |
| 7 | runc/libcontainer/nsenter/ |
C 代码 + Go,进入 namespace 的底层入口 |
runc 总共约 2 万行 Go 代码,是理解容器本质的最佳入口。
第二阶段:看懂 containerd — 理解"容器运行时管理器"
目标:理解 gRPC 服务架构、镜像管理、Snapshot 机制
| 顺序 | 文件 | 内容 |
|---|---|---|
| 1 | containerd/core/ |
核心接口定义,理解 containerd 的数据模型 |
| 2 | containerd/core/containers/ |
Container metadata 增删改查 |
| 3 | containerd/core/content/ |
内容寻址存储(镜像层 blob) |
| 4 | containerd/core/snapshots/ |
文件系统快照管理(overlay 层) |
| 5 | containerd/core/runtime/v2/ |
Runtime v2 接口,shim 管理 |
| 6 | containerd/plugins/services/tasks/ |
Task 服务的 gRPC 实现 |
| 7 | containerd/api/services/ |
Proto 定义(先看 tasks.proto, images.proto) |
| 8 | containerd/cmd/containerd/main.go |
Daemon 启动流程 |
| 9 | containerd/cmd/containerd-shim-runc-v2/ |
Shim 进程实现 |
containerd 是 gRPC 服务化架构的典范,适合学习 Go 服务端编程。
第三阶段:看懂 dockerd (moby) — 理解"Docker 引擎全貌"
目标:理解 Docker 高阶功能(API、网络、卷、Swarm)
| 顺序 | 文件 | 内容 |
|---|---|---|
| 1 | moby/cmd/dockerd/main.go |
Daemon 启动入口 |
| 2 | moby/api/ |
REST API 定义(Swagger + types) |
| 3 | moby/client/ |
Go SDK,对 daemon API 的封装 |
| 4 | moby/daemon/daemon.go |
最核心:Daemon 结构体定义 |
| 5 | moby/daemon/start.go |
容器启动流程 |
| 6 | moby/daemon/container/ |
容器状态机(创建→启动→运行→停止→删除) |
| 7 | moby/daemon/images/ |
镜像管理(pull/push/commit) |
| 8 | moby/daemon/containerd/ |
与 containerd 的集成调用 |
| 9 | moby/daemon/network/ |
网络配置 |
| 10 | moby/daemon/graphdriver/ |
存储驱动(overlay2 等) |
| 11 | moby/daemon/cluster/ |
Swarm 集群模式 |
moby 是最大的仓库(12,700+ 文件),阅读时聚焦 daemon/ 和 api/ 即可。
第四阶段:看懂 docker CLI — 理解"命令行实现"
目标:理解 CLI 到 daemon 的调用模式
| 顺序 | 文件 | 内容 |
|---|---|---|
| 1 | cli/cmd/docker/docker.go |
CLI 入口,cobra 框架 |
| 2 | cli/cli/command/commands/commands.go |
所有子命令注册 |
| 3 | cli/cli/command/container/run.go |
docker run 实现 |
| 4 | cli/cli/command/container/create.go |
docker create 实现 |
| 5 | cli/cli/command/image/build.go |
docker build 实现 |
| 6 | cli/cli/command/inspect/ |
docker inspect |
| 7 | cli/cli/command/formatter/ |
格式化输出 |
cli 是理解 CLI + REST 客户端模式的好参考。
第五阶段:看懂 BuildKit — 理解"镜像构建引擎"
目标:理解 LLB(Low-Level Build)图、Dockerfile 解析、缓存机制
| 顺序 | 文件 | 内容 |
|---|---|---|
| 1 | buildkit/cmd/buildkitd/main.go |
BuildKit daemon 入口 |
| 2 | buildkit/frontend/dockerfile/ |
Dockerfile 前端(解析 → LLB 转换) |
| 3 | buildkit/frontend/dockerfile/parser/ |
Dockerfile 解析器 |
| 4 | buildkit/frontend/dockerfile/dockerfile2llb/ |
Dockerfile → LLB 转换 |
| 5 | buildkit/solver/ |
构建图求解引擎 |
| 6 | buildkit/source/ |
源码拉取(git, http, image) |
| 7 | buildkit/cache/ |
构建缓存管理 |
| 8 | buildkit/exporter/ |
构建产物导出 |
| 9 | buildkit/worker/ |
构建执行 worker |
BuildKit 相对独立,可单独研读。
5. 关键文件速查表
想了解一个 docker run 全流程?
cli/cli/command/container/run.go → CLI 发起命令
moby/client/container_create.go → Go SDK 调用 API
moby/api/server/router/container/ → API Router
moby/daemon/create.go → daemon 创建容器
moby/daemon/start.go → daemon 启动容器
moby/daemon/containerd/image_*.go → 与 containerd 交互
containerd/plugins/services/tasks/ → containerd Task 服务
containerd/core/runtime/v2/ → runtime v2 接口
containerd/cmd/containerd-shim-runc-v2/ → shim 进程
runc/create.go + runc/start.go → OCI runtime 执行
runc/libcontainer/process_linux.go → 进程启动
想了解 docker build 全流程?
cli/cli/command/image/build.go → CLI 发起构建
moby/daemon/builder/builder.go → daemon 构建接口
buildkit/frontend/dockerfile/ → Dockerfile 前端
buildkit/frontend/dockerfile/parser/ → 语法解析
buildkit/frontend/dockerfile/dockerfile2llb/ → 转为 LLB 图
buildkit/solver/ → 图执行引擎
buildkit/source/local/ → 读取 build context
buildkit/cache/ → 缓存管理
buildkit/exporter/ → 导出镜像层
6. 各仓库代码规模
| 仓库 | 文件数(估算) | 语言 | 核心抽象 |
|---|---|---|---|
| moby | ~12,700 | Go | daemon/ — 引擎核心 |
| cli | ~4,000 | Go | cli/command/ — 子命令 |
| containerd | ~6,500 | Go | core/ 接口 + plugins/ 实现 |
| buildkit | ~7,500 | Go | solver/ + frontend/ |
| runc | ~500 | Go + C | libcontainer/ — 容器运行时 |
7. 调试技巧
dockerd 调试模式
dockerd --debug # 开启调试日志
dockerd --log-level=debug # 同上
containerd 调试
ctr --debug tasks ls # ctr 调试模式
手动调 runc
# 创建 rootfs
mkdir -p /tmp/mycontainer/rootfs
docker export $(docker create alpine) | tar -C /tmp/mycontainer/rootfs -xf -
# 生成 OCI spec
runc spec # 生成 config.json
cd /tmp/mycontainer && runc run mycontainer # 启动容器
BuildKit 调试
buildkitd --debug
buildctl --debug build ...
8. 标准与规范
| 标准 | 对应仓库 | 说明 |
|---|---|---|
| OCI Image Spec | 全部 | 镜像格式规范 |
| OCI Runtime Spec | runc | 容器运行时配置规范 |
| OCI Distribution Spec | containerd / moby | 镜像仓库 API 规范 |
| CRI (K8s) | containerd/plugins/cri/ | Kubernetes 容器运行时接口 |
| Compose Spec | cli/cli/compose/ | 多容器编排文件格式 |